package com.monkeyboy.utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
   功能说明：     Class工具类
 @version  1.0.0
 @author 
 @see
 @date  2017年3月15日
 */

public class ClassHelper {
	private static Logger logger = LoggerFactory.getLogger(ClassHelper.class);

	/**
	 * 获取指定包名下的所有类
	 * @param packagename
	 * @return
	 */
	@SuppressWarnings("rawtypes")
	private static List<Class> getAllClass(String packagename) {
		List<Class> list = new ArrayList<Class>();
		ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
		String path = packagename.replace('.', '/');
		URL packageUrl = classLoader.getResource(path);
		if (packageUrl == null) {
			packageUrl = ClassHelper.class.getResource(path);
		}
		if (packageUrl != null) {
			String protocol = packageUrl.getProtocol();
			if (protocol.equals("file")) {
				try {
					ArrayList<File> fileList = new ArrayList<>();
					/**
					 * 这里面的路径使用的是相对路径
					 */
					Enumeration<URL> enumeration = classLoader.getResources(path);
					while (enumeration.hasMoreElements()) {
						URL url = enumeration.nextElement();
						fileList.add(new File(url.getFile()));
					}
					for (int i = 0; i < fileList.size(); i++) {
						list.addAll(findClass(fileList.get(i), packagename));
					}
				} catch (IOException e) {
					e.printStackTrace();
				}
			} else if (protocol.equals("jar")) {
				JarFile jarFile = null;
				try {
					jarFile = ((JarURLConnection) packageUrl.openConnection()).getJarFile();
				} catch (Exception e) {
					e.printStackTrace();
				}

				if (jarFile != null) {
					list = getClassFromJar(jarFile.entries(), packagename, true);
				}
			}
		}
		return list;
	}

	/**
	 * 从JAR包中获取指定包名的所有类
	 * @param jarEntries
	 * @param packageName
	 * @param isRecursion
	 * 是否遍历子包
	 * @return
	 */
	private static List<Class> getClassFromJar(Enumeration<JarEntry> jarEntries, String packageName,
			boolean isRecursion) {
		List<Class> list = new ArrayList<Class>();
		while (jarEntries.hasMoreElements()) {
			JarEntry jarEntry = jarEntries.nextElement();
			if (!jarEntry.isDirectory()) {
				String entryName = jarEntry.getName().replace("/", ".");
				if (entryName.endsWith(".class") && !entryName.contains("$") && entryName.startsWith(packageName)) {
					entryName = entryName.replace(".class", "");
					if (isRecursion) {
						try {
							list.add(Class.forName(entryName));
						} catch (ClassNotFoundException e) {
							e.printStackTrace();
						}
					} else if (!entryName.replace(packageName + ".", "").contains(".")) {
						try {
							list.add(Class.forName(entryName));
						} catch (ClassNotFoundException e) {
							e.printStackTrace();
						}
					}
				}
			}
		}

		return list;
	}

	/**
	 * 如果file是文件夹，则递归调用findClass方法，或者文件夹下的类
	 * 如果file本身是类文件，则加入list中进行保存，并返回
	 * @param file
	 * @param packagename
	 * @return
	 */
	@SuppressWarnings("rawtypes")
	private static List<Class> findClass(File file, String packagename) {
		List<Class> list = new ArrayList<Class>();
		if (!file.exists()) {
			return list;
		}
		File[] files = file.listFiles();
		for (File file2 : files) {
			if (file2.isDirectory()) {
				assert !file2.getName().contains(".");//添加断言用于判断
				List<Class> arrayList = findClass(file2, packagename + "." + file2.getName());
				list.addAll(arrayList);
			} else if (file2.getName().endsWith(".class")) {
				try {
					//保存的类文件不需要后缀.class
					list.add(Class
							.forName(packagename + '.' + file2.getName().substring(0, file2.getName().length() - 6)));
				} catch (ClassNotFoundException e) {
					e.printStackTrace();
				}
			}
		}
		return list;
	}

	/**
	 * 获取实现指定Class接口或继承指定Class父类的所有Class
	 * @param clazz
	 * @return
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public static List<Class> getAllClassByInterface(Class clazz) {
		List<Class> list = new ArrayList<>();
		if (clazz.isInterface()) { //获取指定接口的实现类
			try {
				List<Class> allClass = getAllClass(clazz.getPackage().getName());
				/**
				 * 循环判断路径下的所有类是否实现了指定的接口
				 * 并且排除接口类自己
				 */
				for (int i = 0; i < allClass.size(); i++) {
					/**
					 * 判断是不是同一个接口
					 * isAssignableFrom该方法的解析
					 */
					if (clazz.isAssignableFrom(allClass.get(i))) {
						if (!clazz.equals(allClass.get(i))) {//自身并不加进去
							list.add(allClass.get(i));
						}
					}
				}
			} catch (Exception e) {
				logger.error(String.format("从指定接口获取所有实现接口类出现异常：%s", e.getMessage()));
			}
		} else {//如果不是接口，则获取它的所有子类
			try {
				List<Class> allClass = getAllClass(clazz.getPackage().getName());
				/**
				 * 循环判断路径下的所有类是否继承了指定类
				 * 并且排除父类自己
				 */
				for (int i = 0; i < allClass.size(); i++) {
					/**
					 * isAssignableFrom该方法的解析
					 */
					if (clazz.isAssignableFrom(allClass.get(i))) {
						if (!clazz.equals(allClass.get(i))) {//自身并不加进去
							list.add(allClass.get(i));
						}
					}
				}
			} catch (Exception e) {
				logger.error(String.format("从指定父类获取所有子类出现异常：%s", e.getMessage()));
			}
		}
		return list;
	}
}
